package com.ficus.road.maintain.handler.smartdecision;

import com.ficus.road.maintain.core.model.smartnew.SmartDecisionExcelVO;
import com.ficus.road.maintain.core.model.smartnew.SmartTable;
import com.ficus.road.maintain.core.utils.JsonUtils;
import com.ficus.road.maintain.handler.smartdecision.interfaces.SmartDecisionGroupGenerator;
import com.ficus.road.maintain.handler.smartdecision.interfaces.SmartDecisionMerger;
import com.ficus.road.maintain.util.FormatUtil;
import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author xiawen yang
 * @date 2022/8/11 下午3:43
 */
@Component
public class TableMerger implements SmartDecisionMerger, SmartDecisionGroupGenerator {
    @Override
    public List<SmartTable> merge(List<SmartTable> smartTables) {
        List<SmartTable> mergeByDirection = mergeByDirection(smartTables);
        List<SmartTable> mergeByStake = mergeByStake(mergeByDirection);
        List<SmartTable> mergeByRoad = mergeByRoad(mergeByStake);
        mergeByRoad.forEach(mergedTable -> {
            String uuid = UUID.randomUUID().toString();
            mergedTable.setId(uuid);
            mergedTable.getUnmergedIds().forEach(unmergedId -> {
                smartTables.stream().filter(item -> Objects.equal(item.getId(), unmergedId)).forEach(unmergedTable -> {
                    unmergedTable.setMergedId(uuid);
                });
            });
        });
        return mergeByRoad;
    }

    /**
     * 合并策略：
     * ((上行1), (上行2), (上行3)...) -> 上行
     * ((下行1), (下行2), (下行3)...) -> 下行
     *
     * @param smartTables
     * @return
     */
    private List<SmartTable> mergeByDirection(List<SmartTable> smartTables) {
        List<SmartTable> smartTablesByDirection = new ArrayList<>();
        Map<String, List<SmartTable>> directionMap = smartTables.stream().collect(Collectors.groupingBy(smartTable -> {
            Joiner joiner = Joiner.on("_");
            if (smartTable.getGeneralDirection() == null){
                smartTable.setGeneralDirection("上行");
            }
            return joiner.join(smartTable.getRoadName(), smartTable.getGeneralDirection(), smartTable.getStartStake(), smartTable.getEndStake());
        }));
        directionMap.forEach((id, list) -> {
            SmartTable smartTable = JsonUtils.deepCopy(list.get(0), SmartTable.class);
            double totalWeight = list.stream().mapToDouble(SmartTable::getWeight).sum();
            smartTable.setTotalWeight(FormatUtil.keepDecimalsOfK(totalWeight / list.size(), 3));
            double totalArea = list.stream().mapToDouble(SmartDecisionExcelVO::getArea).sum();
            smartTable.setArea(totalArea);
            setUnmergedIds(list, smartTable);
            smartTablesByDirection.add(smartTable);
        });
        return smartTablesByDirection;
    }

    /**
     * 合并策略：
     * ((上行, 1.5, 3), (下行, 1.5, 3)) -> (上行下行, 1.5, 3)
     *
     * @param smartTables
     * @return
     */
    private List<SmartTable> mergeByStake(List<SmartTable> smartTables) {
        List<SmartTable> smartTablesByStake = new ArrayList<>();
        Joiner directionJoiner = Joiner.on("");
        Map<String, List<SmartTable>> stakeMap = smartTables.stream().collect(Collectors.groupingBy(smartTable -> {
            Joiner idJoiner = Joiner.on("_");
            return idJoiner.join(smartTable.getRoadName(), smartTable.getStartStake(), smartTable.getEndStake());
        }));
        stakeMap.forEach((id, list) -> {
            SmartTable smartTable = JsonUtils.deepCopy(list.get(0), SmartTable.class);
            double totalWeight = list.stream().mapToDouble(SmartTable::getTotalWeight).sum();
            smartTable.setTotalWeight(FormatUtil.keepDecimalsOfK(totalWeight / list.size(), 3));
            double totalArea = list.stream().mapToDouble(SmartDecisionExcelVO::getArea).sum();
            smartTable.setArea(totalArea);
            List<String> directions = list.stream().map(SmartTable::getGeneralDirection).sorted().collect(Collectors.toList());
            smartTable.setDirection(directionJoiner.join(directions));
            setUnmergedIds(list, smartTable);
            smartTablesByStake.add(smartTable);
        });
        return smartTablesByStake;
    }

    /**
     * 合并策略：
     * ((S20, 上行下行, 1, 1.5), (S20, 上行下行, 1.5, 3), (S20, 上行下行, 3, 7.7), (S20, 上行下行, 7.7, 10.145)) -> (S20, 上行下行, 1, 10.145)
     * (S20, 上行, 10.145, 12.786)不与上面的合并
     *
     * @param smartTables
     * @return
     */
    private List<SmartTable> mergeByRoad(List<SmartTable> smartTables) {

        List<SmartTable> tablesWithGroupId = generateGroupId(smartTables, SORTING_GROUP_RULE);
        List<SmartTable> smartTablesByRoad = new ArrayList<>();
        Map<Integer, List<SmartTable>> roadMap = tablesWithGroupId.stream().collect(Collectors.groupingBy(SmartTable::getGroupId));
        roadMap.forEach((id, list) -> {
            SmartTable smartTable = JsonUtils.deepCopy(list.get(0), SmartTable.class);
            double totalWeight = list.stream().mapToDouble(SmartTable::getTotalWeight).sum();
            smartTable.setTotalWeight(FormatUtil.keepDecimalsOfK(totalWeight / list.size(), 3));
            double totalArea = list.stream().mapToDouble(SmartDecisionExcelVO::getArea).sum();
            smartTable.setArea(totalArea);
            double startStake = list.stream().mapToDouble(SmartDecisionExcelVO::getStartStake).min().orElse(0);
            smartTable.setStartStake(startStake);
            double endStake = list.stream().mapToDouble(SmartDecisionExcelVO::getEndStake).max().orElse(0);
            smartTable.setEndStake(endStake);
            smartTable.setLength(FormatUtil.keepDecimalsOfK(endStake - startStake, 3));
            setUnmergedIds(list, smartTable);
            smartTablesByRoad.add(smartTable);
        });
        // 过滤
        return smartTablesByRoad.stream().filter(item ->
                Objects.equal("第一大类", item.getType()) || Objects.equal("第二大类", item.getType()) || !Objects.equal(0d, item.getTotalWeight())
        ).collect(Collectors.toList());
    }

    private void setUnmergedIds(List<SmartTable> smartTables, SmartTable smartTable) {
        Set<String> idSet = smartTables.stream().filter(item -> !Objects.equal("储备库", item.getType())).map(SmartTable::getId).collect(Collectors.toSet());
        Set<String> unmergedIdList1 = smartTable.getUnmergedIdsOrDefault(new HashSet<>());
        Set<String> unmergedIdList2 = smartTables.stream().filter(t -> ObjectUtils.isNotEmpty(t.getUnmergedIds())).flatMap(item -> item.getUnmergedIds().stream()).collect(Collectors.toSet());
        unmergedIdList1.addAll(idSet);
        unmergedIdList1.addAll(unmergedIdList2);
        smartTable.setUnmergedIds(unmergedIdList1);
    }
}
